package com.ncmem.controller;

import com.google.gson.Gson;
import com.ncmem.down2.database.sql.DnFile;
import com.ncmem.up6.biz.WebSafe;
import com.ncmem.up6.model.DnFileInf;
import com.ncmem.up6.store.fastdfs.FastDFSTool;
import com.ncmem.up6.store.FileBlockReader;
import com.ncmem.up6.store.StorageType;
import com.ncmem.up6.utils.ConfigReader;
import com.ncmem.up6.utils.PathTool;
import com.ncmem.up6.utils.WebWriter;
import org.apache.commons.lang.StringUtils;
import org.csource.common.MyException;
import org.csource.fastdfs.StorageClient1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.HashMap;

/**
 * Created by jmzy on 2021/1/6.
 * 路径：
 * down2/clear
 * down2/f_create
 * down2/f_del
 * down2/f_down
 * down2/f_list_cmp
 * down2/f_list
 * down2/f_update
 * down2/fd_data
 */
@RestController
public class DownApi {
    @Autowired
    private HttpServletRequest req;

    @Autowired
    private HttpServletResponse res;

    /**
     * 清空下载数据表，由前端调用
     */
    @RequestMapping(value="down2/clear",method = RequestMethod.GET)
    public void clear() throws SQLException {
        DnFile.build().Clear();
    }

    /**
     * 文件初始化，向数据表添加一条下载记录，由前端调用
     * 调用位置：down.file.js
     * @param id 文件ID
     * @param uid 用户ID
     * @param fdTask 是否是文件夹任务
     * @param nameLoc 文件名称
     * @param pathLoc 文件本地保存路径
     * @param lenSvr 数字化的远程文件大小。1024
     * @param sizeSvr 格式化的远程文件大小。10MB
     * @param cbk JQ回调方法，提供跨域调用
     * @return
     */
    @RequestMapping(value="down2/f_create",method = RequestMethod.GET)
    public String f_create(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="uid", required=false,defaultValue="")String uid,
                           @RequestParam(value="fdTask", required=false,defaultValue="false")boolean fdTask,
                           @RequestParam(value="nameLoc", required=false,defaultValue="")String nameLoc,
                           @RequestParam(value="pathLoc", required=false,defaultValue="")String pathLoc,
                           @RequestParam(value="lenSvr", required=false,defaultValue="")String lenSvr,
                           @RequestParam(value="sizeSvr", required=false,defaultValue="")String sizeSvr,
                           @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws ParseException, IllegalAccessException, SQLException {

            pathLoc = PathTool.url_decode(pathLoc);
            nameLoc	= PathTool.url_decode(nameLoc);//utf-8解码
            sizeSvr = PathTool.url_decode(sizeSvr);

            if (  StringUtils.isEmpty(uid)
                    ||StringUtils.isBlank(pathLoc)
                    ||StringUtils.isBlank(lenSvr))
            {
                return cbk + "({\"value\":null}) ";
            }

            DnFileInf inf = new DnFileInf();
            inf.id	= id;
            inf.uid = uid;
            inf.nameLoc = nameLoc;
            inf.pathLoc = pathLoc;
            inf.lenSvr = Long.parseLong(lenSvr);
            inf.sizeSvr = sizeSvr;
            inf.fdTask = fdTask;

            DnFile.build().Add(inf);

            Gson gson = new Gson();
            String json = gson.toJson(inf);
            try {
                json = URLEncoder.encode(json,"UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            json = json.replaceAll("\\+","%20");
            json = cbk + "({\"value\":\"" + json + "\"})";//返回jsonp格式数据。
            return (json);
    }

    /**
     * 从down_files中删除下载任务，在文件下载完毕后自动调用，前端调用
     * 调用位置：down.file.js
     * @param fid 文件ID
     * @param uid 用户ID
     * @param cbk JQ回调方法，提供跨域调用
     * @return
     */
    @RequestMapping(value="down2/f_del",method = RequestMethod.GET)
    @ResponseBody
    public String f_del(@RequestParam(value="id", required=false,defaultValue="")String fid,
                           @RequestParam(value="uid", required=false,defaultValue="")String uid,
                           @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws SQLException, ParseException {

        if (	StringUtils.isEmpty(uid)||
                StringUtils.isBlank(fid)
                )
        {
            return (cbk + "({\"value\":null})");
        }

        DnFile.build().Delete(fid,uid );
        return (cbk+"({\"value\":1})");
    }

    void FastDFS_down(OutputStream os,String id,long offset,long size)
    {
        StorageClient1 c = FastDFSTool.client();
        byte[] buf = new byte[(int)size];
        try {
            long len = 2048;//5KB/S
            //大于1KB，必须使用流方式下载
            while(len>0)
            {
                buf = c.download_file1(id, offset, len);
                os.write(buf,0,(int)len);
                offset+=len;
                len=(size-offset)>len?len:(size-offset);

                os.write(buf,0,(int)len);
                os.flush();
            }
            //buf = os.toByteArray();
            FastDFSTool.save(c);
        } catch (IOException e) {
            if(c!=null) FastDFSTool.save(c);
            // TODO Auto-generated catch block
            System.out.println("FastDFSTool.down.error");
            e.printStackTrace();
        } catch (MyException e) {
            // TODO Auto-generated catch block
            System.out.println("FastDFSTool.down.error");
            e.printStackTrace();
        }
    }

    /**
     * 下载文件块数据，由控件调用。
     * @param fid 文件ID
     * @param blockIndex    块索引，基于1
     * @param blockOffset   块偏移，相对于整个文件
     * @param blockSize     块大小
     * @param pathSvr       文件地址
     */
    @RequestMapping(value="down2/f_down",method = RequestMethod.GET)
    public void f_down(@RequestHeader(value="id", required=false,defaultValue="")String id,
                       @RequestHeader(value="f_id", required=false,defaultValue="")String fid,
                       @RequestHeader(value="blockIndex", required=false,defaultValue="0")int blockIndex,
                       @RequestHeader(value="blockOffset", required=false,defaultValue="0")Long blockOffset,
                       @RequestHeader(value="blockSize", required=false,defaultValue="0")int blockSize,
                       @RequestHeader(value="pathSvr", required=false,defaultValue="")String pathSvr,
                       @RequestHeader(value="object_key", required=false,defaultValue="")String object_key,
                       @RequestHeader(value="token", required=false,defaultValue="")String token
    )
    {
        pathSvr = PathTool.url_decode(pathSvr);

        if (
                StringUtils.isBlank(id)||
                StringUtils.isBlank(pathSvr) )
        {
            res.setStatus(500);
            res.setHeader("err","参数为空");
            String msg = String.format("fid:%s,blockIndex:%s,blockOffset:%s,blockSize:%s,pathSvr:%s",
                    fid,
                    blockIndex,
                    blockOffset,
                    blockSize,
                    pathSvr);
            try {
                res.getOutputStream().write(msg.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }

        WebSafe ws = new WebSafe();
        if(ws.downToken(token,id,blockIndex,blockOffset))
        {
            HashMap msg = new HashMap();
            msg.put("err-msg", "token valid fail");
            msg.put("tokenRecv",token);
            msg.put("id",id);
            msg.put("blockIndex",blockIndex);
            msg.put("blockOffset",blockOffset);
            res.setStatus(500);
            res.setHeader("err","token valid fail");
            try {
                Gson g = new Gson();
                res.getOutputStream().write(g.toJson(msg).getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }

        File f = new File(pathSvr);
        //文件不存在
        if(!f.exists()&& ConfigReader.storageType()== StorageType.IO)
        {
            res.setStatus(500);
            OutputStream os = null;
            try {
                os = res.getOutputStream();
                System.out.println(String.format("%s 文件不存在",pathSvr));
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }

        //文件存在
        long fileLen = f.length();
        res.setContentType("application/x-download");
        res.setHeader("Pragma","No-cache");
        res.setHeader("Cache-Control","no-cache");
        res.addHeader("Content-Length",Integer.toString(blockSize));
        res.setDateHeader("Expires", 0);

        OutputStream os=null;
        try
        {
            os = res.getOutputStream();
            //文件块读取器
            FileBlockReader fr = ConfigReader.blockReader();
            if(StorageType.FastDFS==fr.storage||
                    StorageType.OBS == fr.storage||
                    StorageType.OSS == fr.storage||
                    StorageType.COS == fr.storage||
                    StorageType.Minio == fr.storage) {
                pathSvr = object_key;
            }
            else if(StorageType.IO==fr.storage){
                if(!PathTool.exist(pathSvr))
                {
                    os.close();
                    res.setStatus(500);
                    return;
                }
            }

            WebWriter ww = new WebWriter();
            if(!ww.write(fr,os,pathSvr,blockOffset,blockSize))
            {
                res.setStatus(500);
            }

            os = null;
            res.flushBuffer();
        }
        catch(IOException e)
        {
            res.setStatus(500);
            try {
                os.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
        finally
        {
            if(os != null)
            {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                os = null;
            }
        }
    }

    /**
     * 列出已经上传完的文件列表，在打开页面时调用，由前端调用
     * @param uid 用户ID
     * @param cbk jq回调方法，提供跨域调用
     */
    @RequestMapping(value="down2/f_list_cmp",method = RequestMethod.GET)
    public String f_list_cmp(@RequestParam(value="uid", required=false,defaultValue="")String uid,
                       @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws SQLException, InstantiationException, ParseException, IllegalAccessException {
        if (!StringUtils.isEmpty(uid))
        {
            String json = DnFile.build().all_complete(uid);
            if(!StringUtils.isBlank(json))
            {
                try {
                    json = URLEncoder.encode(json,"utf-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                json = json.replace("+","%20");
                return cbk + "({\"value\":\""+json+"\"})";
            }
        }
        return (cbk+"({\"value\":null})");
    }

    /**
     * 列出未下载完的文件列表，在打开下载页面时调用，由前端调用
     * @param uid 用户ID
     * @param cbk jq回调方法，提供跨域调用
     * @return
     */
    @RequestMapping(value="down2/f_list",method = RequestMethod.GET)
    public String f_list(@RequestParam(value="uid", required=false,defaultValue="")String uid,
                             @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws SQLException, InstantiationException, ParseException, IllegalAccessException {

        if (!StringUtils.isEmpty(uid))
        {
            String json = DnFile.build().all_uncmp( uid);

            if(!StringUtils.isBlank(json))
            {
                try {
                    json = URLEncoder.encode(json,"utf-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                json = json.replaceAll("\\+","%20");//
                json = cbk + "({\"value\":\""+json+"\"})";
                return json;
            }
        }

        return cbk + "({\"value\":null})";
    }

    /**
     * 更新文件下载进度，由前端调用。
     * @param id 文件ID
     * @param uid 用户ID
     * @param lenLoc 已下载大小
     * @param per 已下载百分比
     * @param cbk jq回调方法，提供跨域调用。
     * @return
     */
    @RequestMapping(value="down2/f_update",method = RequestMethod.GET)
    public String f_update(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="uid", required=false,defaultValue="")String uid,
                           @RequestParam(value="lenLoc", required=false,defaultValue="")String lenLoc,
                           @RequestParam(value="perLoc", required=false,defaultValue="")String per,
                         @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws ParseException, IllegalAccessException, SQLException {
        per = per.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
        per = PathTool.url_decode(per);

        if (
                StringUtils.isEmpty(uid)||
                StringUtils.isEmpty(id)||
                StringUtils.isEmpty(cbk))
        {
            return cbk + "({\"value\":0})";
        }

        DnFile.build().process(id,uid,lenLoc,per);
        return cbk + "({\"value\":1})";
    }

    /**
     * 列出文件夹数据，由前端调用
     * 调用位置：down.folder.js
     * @param id 文件夹ID
     * @param cbk JQ回调方法，提供跨域调用
     * @return
     */
    @RequestMapping(value="down2/fd_data",method = RequestMethod.GET)
    @ResponseBody
    public String fd_data(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws SQLException, InstantiationException, ParseException, IllegalAccessException {
        String json = cbk + "({\"value\":null})";

        if (  !StringUtils.isEmpty(id)	)
        {
            String data = DnFile.build().childs(id);
            //XDebug.Output("文件列表",data);
            try {
                data = URLEncoder.encode(data,"utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            data = data.replace("+","%20");
            json = cbk + "({\"value\":\""+data+"\"})";
        }
        return json;
    }
}
